(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var _w = window, _b = document.body, _d = document.documentElement;
    function getPoint(e) {
        var scrollX = Math.max(0, _w.pageXOffset || _d.scrollLeft || _b.scrollLeft || 0) - (_d.clientLeft || 0), scrollY = Math.max(0, _w.pageYOffset || _d.scrollTop || _b.scrollTop || 0) - (_d.clientTop || 0), pointX = e ? (Math.max(0, e.pageX || e.clientX || 0) - scrollX) : 0, pointY = e ? (Math.max(0, e.pageY || e.clientY || 0) - scrollY) : 0;
        return { x: pointX, y: pointY };
    }
    let errors = {
        arugmentNull(name) {
            return new Error(`Argument ${name} is null or empty.`);
        }
    };
    class Sortable {
        constructor(container, options) {
            this.hoverInsertClassName = "hover-insert";
            this.hoverAppendClassName = "hover-append";
            if (!container)
                throw errors.arugmentNull("container");
            this._container = container;
            this._options = options || {}; /* nothing atm */
            this._clickItem = null;
            this._dragItem = null;
            this._hovItem = null;
            this._sortLists = [];
            this._click = {};
            this._dragging = false;
            this._container.style.position = "static";
            window.addEventListener("mousedown", this.onPress.bind(this), true);
            window.addEventListener("touchstart", this.onPress.bind(this), true);
            window.addEventListener("mouseup", (e) => this.onRelease(e), true);
            window.addEventListener("touchend", (e) => this.onRelease(e), true);
            window.addEventListener("mousemove", this.onMove.bind(this), true);
            window.addEventListener("touchmove", this.onMove.bind(this), true);
        }
        toArray(attr) {
            attr = attr || "id";
            var data = [], item = null, uniq = "";
            for (var i = 0; i < this._container.children.length; ++i) {
                item = this._container.children[i],
                    uniq = item.getAttribute(attr) || "";
                uniq = uniq.replace(/[^0-9]+/gi, "");
                data.push(uniq);
            }
            return data;
        }
        // serialize order array into a string 
        toString(attr, delimiter) {
            delimiter = delimiter || ":";
            return this.toArray(attr).join(delimiter);
        }
        // checks if mouse x/y is on top of an item 
        _isOnTop(item, x, y) {
            var box = item.getBoundingClientRect(), isx = (x > box.left && x < (box.left + box.width)), isy = (y > box.top && y < (box.top + box.height));
            return (isx && isy);
        }
        _isBelow(item, x, y) {
            var box = item.getBoundingClientRect();
            var isx = (x > box.left && x < (box.left + box.width));
            var isy = y > (box.top + box.height);
            return isx && isy;
        }
        // manipulate the className of an item (for browsers that lack classList support)
        _itemClass(item, task, className) {
            var list = item.className.split(/\s+/), index = list.indexOf(className);
            if (task === "add" && index == -1) {
                list.push(className);
                item.className = list.join(" ");
            }
            else if (task === "remove" && index != -1) {
                list.splice(index, 1);
                item.className = list.join(" ");
            }
        }
        // update item position 
        _moveItem(item, x, y) {
            item.style["-webkit-transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
            item.style["-moz-transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
            item.style["-ms-transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
            item.style["transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
        }
        // make a temp fake item for dragging and add to container 
        _makeDragItem(item) {
            this._trashDragItem();
            // this._sortLists = document.querySelectorAll("[data-is-sortable]");
            this._sortLists = this._options.connector || []; //(this._options.connect || []).map(o => o._container);
            this._sortLists.push(this._container);
            this._clickItem = item;
            this._itemClass(this._clickItem, "add", "active");
            this._dragItem = document.createElement(item.tagName);
            this._dragItem.className = "dragging";
            this._dragItem.innerHTML = item.innerHTML;
            this._dragItem.style.position = "absolute";
            this._dragItem.style.zIndex = "9999";
            this._dragItem.style.left = (item.offsetLeft || 0) + "px";
            this._dragItem.style.top = (item.offsetTop || 0) + "px";
            this._dragItem.style.width = (item.offsetWidth || 0) + "px";
            this._container.appendChild(this._dragItem);
        }
        // remove drag item that was added to container 
        _trashDragItem() {
            if (this._dragItem && this._clickItem) {
                this._itemClass(this._clickItem, "remove", "active");
                this._clickItem = null;
                this._container.removeChild(this._dragItem);
                this._dragItem = null;
            }
        }
        // on item press/drag 
        onPress(e) {
            if (e.target == null)
                return;
            let itemElement = e.target;
            while (itemElement.parentNode != this._container && itemElement.parentNode != null) {
                itemElement = itemElement.parentNode;
            }
            if (itemElement.parentNode === this._container) {
                e.preventDefault();
                this._dragging = true;
                this._click = getPoint(e);
                this._makeDragItem(itemElement);
                this.onMove(e);
            }
        }
        // on item release/drop 
        onRelease(e) {
            this._dragging = false;
            let p = this._clickItem ? this._clickItem.parentNode : null;
            let isCurrent = false;
            while (p) {
                if (this._container == p) {
                    isCurrent = true;
                    break;
                }
                p = p.parentNode;
            }
            if (isCurrent && this._options.onRelease) {
                let itemIndex;
                if (this._hovItem != null && this._hovItem.className.indexOf(this.hoverInsertClassName) >= 0) {
                    for (let i = 0; i < this._hovItem.parentNode.children.length; i++) {
                        if (this._hovItem.parentNode.children[i] == this._hovItem) {
                            itemIndex = i;
                            break;
                        }
                    }
                }
                this._options.onRelease.bind(this)(this._hovItem, this._clickItem, itemIndex);
            }
            if (this._hovItem) {
                this._hovItem.className = "";
            }
            this._trashDragItem();
        }
        // on item drag/move
        onMove(e) {
            if (!this._dragItem || !this._dragging)
                return;
            e.preventDefault();
            var point = getPoint(e);
            var container;
            // drag fake item 
            this._moveItem(this._dragItem, (point.x - this._click.x), (point.y - this._click.y));
            // keep an eye for other sortable lists and switch over to it on hover 
            for (var a = 0; a < this._sortLists.length; ++a) {
                var subContainer = this._sortLists[a];
                if (this._isOnTop(subContainer, point.x, point.y)) {
                    container = subContainer;
                }
            }
            if (container == null) {
                return;
            }
            /*
            // container is empty, move clicked item over to it on hover
            if (this._isOnTop(container, point.x, point.y) && container.children.length === 0) {
                container.appendChild(this._clickItem);
                return;
            }
            */
            let latestItem = null;
            if (container == this._container) {
                if (container.children.length > 1) {
                    // 最后一个子元素是 dragItem
                    latestItem = container.children[container.children.length - 2];
                }
            }
            else {
                latestItem = container.children[container.children.length - 1];
            }
            if (this._hovItem) {
                this._hovItem.className = "";
            }
            for (var b = 0; b < container.children.length; ++b) {
                var subItem = container.children[b];
                if (subItem === this._clickItem || subItem === this._dragItem) {
                    continue;
                }
                if (this._isOnTop(subItem, point.x, point.y)) {
                    this._hovItem = subItem;
                    this._itemClass(this._hovItem, "add", this.hoverInsertClassName);
                }
                else if (this._hovItem != null && latestItem != null && this._isBelow(latestItem, point.x, point.y)) {
                    this._itemClass(this._hovItem, "add", this.hoverAppendClassName);
                }
            }
        }
    }
    exports.Sortable = Sortable;
    class Dragable extends Sortable {
        onMove(e) {
            if (!this._dragItem || !this._dragging)
                return;
            e.preventDefault();
            var point = getPoint(e);
            var container;
            // drag fake item 
            this._moveItem(this._dragItem, (point.x - this._click.x), (point.y - this._click.y));
            // keep an eye for other sortable lists and switch over to it on hover 
            for (var a = 0; a < this._sortLists.length; ++a) {
                var subContainer = this._sortLists[a];
                if (this._isOnTop(subContainer, point.x, point.y)) {
                    container = subContainer;
                }
            }
            if (container == this._container) {
                return;
            }
            super.onMove(e);
        }
    }
    exports.Dragable = Dragable;
});
